package com.yh.csx.etl.util;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * 日期工具类
 * 
 * @author Robin.Wang
 * @date 2020-03-20
 *
 */
public class DateUtils {

    private static final Logger log = LoggerFactory.getLogger(DateUtils.class);
    // private static final String ETL_PERIOD_PATTEN = "";
    private static final Pattern ETL_PERIOD_PATTEN = Pattern
            .compile("^([1-9][0-9]*)(day|year|month|hour|minute|second)$");
    public final static String timeZone; // 当前时区
    private static DateTimeZone dateTimeZone;

    static {
        TimeZone localTimeZone = TimeZone.getDefault();
        int rawOffset = localTimeZone.getRawOffset();
        String symbol = "+";
        if (rawOffset < 0) {
            symbol = "-";
        }
        rawOffset = Math.abs(rawOffset);
        int offsetHour = rawOffset / 3600000;
        int offsetMinute = rawOffset % 3600000 / 60000;
        String hour = String.format("%1$02d", offsetHour);
        String minute = String.format("%1$02d", offsetMinute);
        timeZone = symbol + hour + ":" + minute;
        dateTimeZone = DateTimeZone.forID(timeZone);
        TimeZone.setDefault(TimeZone.getTimeZone("GMT" + timeZone));
    }

    /**
     * 通用日期时间字符解析
     *
     * @param datetimeStr 日期时间字符串
     * @return Date
     */
    public static Date parseDate(String datetimeStr) {
        if (StringUtils.isEmpty(datetimeStr)) {
            return null;
        }
        datetimeStr = datetimeStr.trim();
        if (datetimeStr.contains("-")) {
            if (datetimeStr.contains(":")) {
                datetimeStr = datetimeStr.replace(" ", "T");
            }
        } else if (datetimeStr.contains(":")) {
            datetimeStr = "T" + datetimeStr;
        }

        DateTime dateTime = new DateTime(datetimeStr, dateTimeZone);

        return dateTime.toDate();
    }

    private static LoadingCache<String, DateTimeFormatter> dateFormatterCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<String, DateTimeFormatter>() {

                @Override
                public DateTimeFormatter load(String key) {
                    return DateTimeFormatter.ofPattern(key);
                }
            });

    /**
     * 解析时间
     * 
     */
    public static Date parseDate2(String datetimeStr) {
        if (StringUtils.isEmpty(datetimeStr)) {
            return null;
        }
        try {
            datetimeStr = datetimeStr.trim();
            int len = datetimeStr.length();
            if (datetimeStr.contains("-") && datetimeStr.contains(":") && datetimeStr.contains(".")) {
                // 包含日期+时间+毫秒
                // 取毫秒位数
                int msLen = len - datetimeStr.indexOf(".") - 1;
                StringBuilder ms = new StringBuilder();
                for (int i = 0; i < msLen; i++) {
                    ms.append("S");
                }
                String formatter = "yyyy-MM-dd HH:mm:ss." + ms;

                DateTimeFormatter dateTimeFormatter = dateFormatterCache.get(formatter);
                LocalDateTime dateTime = LocalDateTime.parse(datetimeStr, dateTimeFormatter);
                return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
            } else if (datetimeStr.contains("-") && datetimeStr.contains(":")) {
                // 包含日期+时间
                // 判断包含时间位数
                int i = datetimeStr.indexOf(":");
                i = datetimeStr.indexOf(":", i + 1);
                String formatter;
                if (i > -1) {
                    formatter = "yyyy-MM-dd HH:mm:ss";
                } else {
                    formatter = "yyyy-MM-dd HH:mm";
                }

                DateTimeFormatter dateTimeFormatter = dateFormatterCache.get(formatter);
                LocalDateTime dateTime = LocalDateTime.parse(datetimeStr, dateTimeFormatter);
                return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
            } else if (datetimeStr.contains("-")) {
                // 只包含日期
                String formatter = "yyyy-MM-dd";
                DateTimeFormatter dateTimeFormatter = dateFormatterCache.get(formatter);
                LocalDate localDate = LocalDate.parse(datetimeStr, dateTimeFormatter);
                return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
            } else if (datetimeStr.contains(":")) {
                // 只包含时间
                String formatter;
                if (datetimeStr.contains(".")) {
                    // 包含毫秒
                    int msLen = len - datetimeStr.indexOf(".") - 1;
                    StringBuilder ms = new StringBuilder();
                    for (int i = 0; i < msLen; i++) {
                        ms.append("S");
                    }
                    formatter = "HH:mm:ss." + ms;
                } else {
                    // 判断包含时间位数
                    int i = datetimeStr.indexOf(":");
                    i = datetimeStr.indexOf(":", i + 1);
                    if (i > -1) {
                        formatter = "HH:mm:ss";
                    } else {
                        formatter = "HH:mm";
                    }
                }
                DateTimeFormatter dateTimeFormatter = dateFormatterCache.get(formatter);
                LocalTime localTime = LocalTime.parse(datetimeStr, dateTimeFormatter);
                LocalDate localDate = LocalDate.of(1970, Month.JANUARY, 1);
                LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
                return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
            }
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

    /**
     * 
     * 时间格式化
     * 
     * @throws Exception
     */

    public static String formatValue(String format, Date date) throws Exception {
        if (StringUtils.isEmpty(format)) {
            throw new Exception("format can't be empty");
        }
        try {
            Instant instant = date.toInstant();
            ZoneId zoneId = ZoneId.systemDefault();
            LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
            DateTimeFormatter dateTimeFormatter = dateFormatterCache.get(format);
            return dateTimeFormatter.format(localDateTime);
        } catch (ExecutionException e) {
            log.error("date format error:", e);
        }
        return null;
    }

    public static long getSecond(String period) throws Exception {
        Matcher matcher = ETL_PERIOD_PATTEN.matcher(period);
        if (matcher == null) {
            throw new Exception(period + "is not match the required pattern ");
        }
        while (matcher.find()) {
            Calendar c = Calendar.getInstance();
            String count = matcher.group(1);
            String unit = matcher.group(2);
            if ("year".equals(unit)) {
                c.add(Calendar.YEAR, -Integer.valueOf(count));
                return (new Date().getTime() - c.getTime().getTime()) / 1000;
            } else if ("month".equals(unit)) {
                c.add(Calendar.MONTH, -Integer.valueOf(count));
                return (new Date().getTime() - c.getTime().getTime()) / 1000;
            } else if ("day".equals(unit)) {
                c.add(Calendar.DAY_OF_YEAR, -Integer.valueOf(count));
                return (new Date().getTime() - c.getTime().getTime()) / 1000;
            } else if ("hour".equals(unit)) {
                c.add(Calendar.HOUR_OF_DAY, -Integer.valueOf(count));
                return (new Date().getTime() - c.getTime().getTime()) / 1000;
            } else if ("minute".equals(unit)) {
                c.add(Calendar.MINUTE, -Integer.valueOf(count));
                return (new Date().getTime() - c.getTime().getTime()) / 1000;
            } else if ("second".equals(unit)) {
                c.add(Calendar.SECOND, -Integer.valueOf(count));
                return (new Date().getTime() - c.getTime().getTime()) / 1000;
            }
        }
        return 0;
    }

    /**
     * 获取指定周期前的日期
     * 
     * @param period "1year,3month,30day" 格式
     * @return Date 日期
     * @throws Exception
     */
    public static Date getDateBeforePeriod(String period) throws Exception {

        Matcher matcher = ETL_PERIOD_PATTEN.matcher(period);
        if (matcher == null) {
            throw new Exception(period + "is not match the required pattern ");
        }
        while (matcher.find()) {
            Calendar c = Calendar.getInstance();
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, 0);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);

            String count = matcher.group(1);
            String unit = matcher.group(2);
            if ("year".equals(unit)) {
                c.add(Calendar.YEAR, -Integer.valueOf(count));
                return c.getTime();
            } else if ("month".equals(unit)) {
                c.add(Calendar.MONTH, -Integer.valueOf(count));
                return c.getTime();
            } else if ("day".equals(unit)) {
                c.add(Calendar.DAY_OF_YEAR, -Integer.valueOf(count));
                return c.getTime();
            } else if ("hour".equals(unit)) {
                c.add(Calendar.HOUR, -Integer.valueOf(count));
                return c.getTime();
            } else if ("minute".equals(unit)) {
                c.add(Calendar.MINUTE, -Integer.valueOf(count));
                return c.getTime();
            } else if ("second".equals(unit)) {
                c.add(Calendar.SECOND, -Integer.valueOf(count));
                return c.getTime();
            }
        }
        return new Date();
    }

    /**
     * 获取指定日期前一段时间的日期
     * 
     * @param date   指定日志
     * @param period 指定时间段
     * @return Date 返回日期
     * 
     */
    public static Date getDateBeforePeriods(Date date, String period) throws Exception {
        Matcher matcher = ETL_PERIOD_PATTEN.matcher(period);
        if (matcher == null) {
            throw new Exception(period + "is not match the required pattern ");
        }
        while (matcher.find()) {
            Calendar c = Calendar.getInstance();
            c.setTime(date);
            String count = matcher.group(1);
            String unit = matcher.group(2);
            if ("year".equals(unit)) {
                c.add(Calendar.YEAR, -Integer.valueOf(count));
                return c.getTime();
            } else if ("month".equals(unit)) {
                c.add(Calendar.MONTH, -Integer.valueOf(count));
                return c.getTime();
            } else if ("day".equals(unit)) {
                c.add(Calendar.DAY_OF_YEAR, -Integer.valueOf(count));
                return c.getTime();
            } else if ("hour".equals(unit)) {
                c.add(Calendar.HOUR, -Integer.valueOf(count));
                return c.getTime();
            } else if ("minute".equals(unit)) {
                c.add(Calendar.MINUTE, -Integer.valueOf(count));
                return c.getTime();
            } else if ("second".equals(unit)) {
                c.add(Calendar.SECOND, -Integer.valueOf(count));
                return c.getTime();
            }
        }

        return new Date();
    }

}
